Robolectric Test HTTP

手機的使用情境,常常會跟網路息息相關。

舉例來說:

  • 看網頁
  • 下載檔案
  • 調用API
  • 等等

這邊在測試的時候,當然是想越貼近實際狀況越好,所以有可能拿實際網站來 Test。

又或者實際網站必須常常改版等等的變因,迫使你自己架一個假網站來 Test。

接下來,我們來看看使用 Robolectric 的話,可以怎樣 Test。


PS:映象中,Robolectric 只能測試 Apache Lib 的網路連線,在你發出的 request還沒到真實 Server 之前,就先攔截下來。

這邊先介紹一些 Robolectric 提供的方法,首先是logHttpRequests,預設情況為silence,打開後可以在 output 看到一些 log

  • Robolectric.getFakeHttpLayer().logHttpRequests()
  • Robolectric.getFakeHttpLayer().silence()

接著是 Robolectric 攔截你的 request 的開關。預設當然是攔截狀態。

  • Robolectric.getFakeHttpLayer().interceptHttpRequests(boolean)
  • Robolectric.getFakeHttpLayer().interceptResponseContent(boolean)

Environment:

  • Robolectric Ver. 2.4
  • Mac OS X 10.10
  • Android Studio 1.0.2

總算可以開始測試了!

一樣準備好 SetUp 和 TearDown 方法。

這次只先準備兩個 response,200 和 404。

HttpTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import android.os.Environment;

import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.FileAsyncHttpResponseHandler;
import com.loopj.android.http.SyncHttpClient;
import com.maxwin.busstop.advance.YumeFile;

import junit.framework.TestCase;

import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.util.EntityUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.HttpResponseGenerator;
import org.robolectric.tester.org.apache.http.FakeHttpLayer;
import org.robolectric.tester.org.apache.http.HttpEntityStub;
import org.robolectric.tester.org.apache.http.RequestMatcher;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

@RunWith(RobolectricTestRunner.class)
@Config(emulateSdk = 18)
public class Temp2Test extends TestCase {

HttpResponse successResponse;
HttpResponse failResponse;

@Before
public void setUp() throws Exception {
super.setUp();

ProtocolVersion httpProtocolVersion = new ProtocolVersion("HTTP", 1, 1);

successResponse = new BasicHttpResponse(httpProtocolVersion, 200, "OK");
successResponse.setEntity(new StringEntity("Yume S"));

failResponse = new BasicHttpResponse(httpProtocolVersion, 404, "Not Found");
failResponse.setEntity(new StringEntity("Yume F"));
}

@After
public void tearDown() throws Exception {
Robolectric.clearHttpResponseRules();
Robolectric.clearPendingHttpResponses();
super.tearDown();
}
}

現在我們開始寫第一組 Test,用 Robolectric 的 addHttpResponseRule。

這一組測試都是側相同的東西,可快速看過。

如果你有興趣的話可以多花一些時間在第 4th and 5th 的測試。

Robolectric.addHttpResponseRule

  • (String method, String uri, HttpResponse response)
  • (String uri, HttpResponse response)
  • (String uri, String response)
  • (RequestMatcher requestMatcher, HttpResponse response)
  • (RequestMatcher requestMatcher, List<? extends HttpResponse> responses)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
@Test
public void testHttpResponseRule1() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/test1.html";

Robolectric.addHttpResponseRule("GET",url,successResponse);

assertFalse(Robolectric.httpRequestWasMade());
assertFalse(Robolectric.httpRequestWasMade(url));

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

@Test
public void testHttpResponseRule2() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/test2.html";

Robolectric.addHttpResponseRule(url,successResponse);

assertFalse(Robolectric.httpRequestWasMade());
assertFalse(Robolectric.httpRequestWasMade(url));

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

@Test
public void testHttpResponseRule3() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/test3.html";

Robolectric.addHttpResponseRule(url,"Yume S");

assertFalse(Robolectric.httpRequestWasMade());
assertFalse(Robolectric.httpRequestWasMade(url));

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

@Test
public void testHttpResponseRule4() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/test4.html";

RequestMatcher requestMatcher = new FakeHttpLayer.DefaultRequestMatcher("GET","http://www.test.com/test4.html");
Robolectric.addHttpResponseRule(requestMatcher,successResponse);

assertFalse(Robolectric.httpRequestWasMade());
assertFalse(Robolectric.httpRequestWasMade(url));

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

@Test
public void testHttpResponseRule5() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/test5.html";

RequestMatcher requestMatcher = new FakeHttpLayer.RequestMatcherBuilder()
.host("www.test.com")
.path("test5.html")
.method("GET");
List<HttpResponse> list = new ArrayList<>();
list.add(successResponse);
list.add(failResponse);
Robolectric.addHttpResponseRule(requestMatcher,list);

assertFalse(Robolectric.httpRequestWasMade());
assertFalse(Robolectric.httpRequestWasMade(url));

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

response = client.execute(request);
assertEquals(404,response.getStatusLine().getStatusCode());
assertEquals("Yume F", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

接著是第二組測試。

連續發了三次 request 之後,分別會收到 200 404 200

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
@Test
public void testPendingHttpResponse1() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/testP1.html";

Robolectric.addPendingHttpResponse(successResponse);
Robolectric.addPendingHttpResponse(failResponse);
Robolectric.addPendingHttpResponse(successResponse);

assertFalse(Robolectric.httpRequestWasMade());
assertFalse(Robolectric.httpRequestWasMade(url));

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

response = client.execute(request);
assertEquals(404,response.getStatusLine().getStatusCode());
assertEquals("Yume F", EntityUtils.toString(response.getEntity()));

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

@Test
public void testPendingHttpResponse2() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/testP2.html";

HttpResponseGenerator successHttpResponseGenerator = new HttpResponseGenerator() {
@Override
public HttpResponse getResponse(HttpRequest request) {
return successResponse;
}
};
HttpResponseGenerator failHttpResponseGenerator = new HttpResponseGenerator() {
@Override
public HttpResponse getResponse(HttpRequest request) {
return failResponse;
}
};

Robolectric.addPendingHttpResponse(successHttpResponseGenerator);
Robolectric.addPendingHttpResponse(failHttpResponseGenerator);
Robolectric.addPendingHttpResponse(successHttpResponseGenerator);

assertFalse(Robolectric.httpRequestWasMade());
assertFalse(Robolectric.httpRequestWasMade(url));

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

response = client.execute(request);
assertEquals(404,response.getStatusLine().getStatusCode());
assertEquals("Yume F", EntityUtils.toString(response.getEntity()));

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

@Test
public void testPendingHttpResponse3() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/testP3.html";

Header[] headers = new Header[1];
headers[0] = new BasicHeader("Yume","Dream");

Robolectric.addPendingHttpResponse(200,"Yume S",headers);
Robolectric.addPendingHttpResponse(404,"Yume F",headers);
Robolectric.addPendingHttpResponse(200,"Yume S",headers);

assertFalse(Robolectric.httpRequestWasMade());
assertFalse(Robolectric.httpRequestWasMade(url));

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

response = client.execute(request);
assertEquals(404,response.getStatusLine().getStatusCode());
assertEquals("Yume F", EntityUtils.toString(response.getEntity()));

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

又回來寫第一組測試, 6th 寫法。

先寫一個 ResponseRule。

MyResponseRule.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyResponseRule implements HttpEntityStub.ResponseRule {
@Override
public HttpResponse getResponse() throws HttpException, IOException {
HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 200, "OK");

response.addHeader(new BasicHeader("Yume","Dream"));

response.setEntity(new StringEntity("Yume S"));

return response;
}

@Override
public boolean matches(HttpRequest request) {
String uri = request.getRequestLine().getUri();
if (uri.equals("http://www.test.com/test6.html"))
return true;
return false;
}
}

接下來三個測試:

  • 用 DefaultHttpClient
  • 用 SyncHttpClient 且 request 成功
  • 用 SyncHttpClient 且 request 失敗
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Test
public void testHttpResponseRule6DefaultHttpClient() throws Exception {
HttpClient client = new DefaultHttpClient();
String url = "http://www.test.com/test6.html";

Robolectric.getFakeHttpLayer().addHttpResponseRule(new MyResponseRule());

URI uri = new URI( url);
HttpGet request = new HttpGet(uri);
HttpResponse response;

response = client.execute(request);
assertEquals(200,response.getStatusLine().getStatusCode());
assertEquals("Yume S", EntityUtils.toString(response.getEntity()));

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

@Test
public void testHttpResponseRule6SyncHttpClientSuccess() throws Exception {
String url = "http://www.test.com/test6.html";

Robolectric.getFakeHttpLayer().addHttpResponseRule(new MyResponseRule());

SyncHttpClient syncHttpClient = new SyncHttpClient();
syncHttpClient.get(url,new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try {
String decoded = new String(responseBody, "UTF-8");
assertEquals(200,statusCode);
assertEquals("Yume S", decoded);
} catch (UnsupportedEncodingException e) {
fail("never happen");
}
}

@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
fail("never happen");
}
});

assertTrue(Robolectric.httpRequestWasMade());
assertTrue(Robolectric.httpRequestWasMade(url));
}

@Test
public void testHttpResponseRule6SyncHttpClientFail() throws Exception {
String url = "http://www.test.com/test7.html";

Robolectric.getFakeHttpLayer().addHttpResponseRule(new MyResponseRule());

SyncHttpClient syncHttpClient = new SyncHttpClient();
syncHttpClient.get(url,new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
fail("never happen");
}

@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
System.out.print("Request Fail Happen");
}
});
}

最後我們來測一下 Download file。

我先在專案的 assets 中放著我想要測試的 file。

事先當然已知檔案大小以及 checksum。

下載完之後確認檔案大小以及他的完整性。

最後把測試目錄清除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Test
public void testDownloadFile1() throws Exception {
String url = "http://www.test.com/test.js";

HttpResponse downloadResponse = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
InputStream is = Robolectric.getShadowApplication().getAssets().open("test/async_data_finder_factory.js");
downloadResponse.setEntity(new InputStreamEntity(is,737));
Robolectric.addHttpResponseRule(url,downloadResponse);

String folderName = Robolectric.getShadowApplication().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
YumeFile folder = new YumeFile(folderName);
folder.mkdirs();
YumeFile file = new YumeFile(folderName + "/" + "b.js");

SyncHttpClient syncHttpClient = new SyncHttpClient();
syncHttpClient.get(url,new FileAsyncHttpResponseHandler(file) {
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
fail("never happen");
}

@Override
public void onSuccess(int statusCode, Header[] headers, File file) {
YumeFile yumeFile = new YumeFile(file.getAbsolutePath());
assertEquals(yumeFile.checksum(),"065710dfd1e9419deb9507d2e7fdaa98");
assertEquals(737,file.length());
}
});

File externalFileFolder = Robolectric.getShadowApplication().getExternalFilesDir("").getParentFile();
externalFileFolder.delete();
}

@Test
public void testDownloadFile2() throws Exception {
String url = "http://www.test.com/test.ttf";

HttpResponse downloadResponse = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
InputStream is = Robolectric.getShadowApplication().getAssets().open("test/RobotoCondensed-Bold.ttf");
downloadResponse.setEntity(new InputStreamEntity(is,737));
Robolectric.addHttpResponseRule(url,downloadResponse);

String folderName = Robolectric.getShadowApplication().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
YumeFile folder = new YumeFile(folderName);
folder.mkdirs();
YumeFile file = new YumeFile(folderName + "/" + "c.ttf");

SyncHttpClient syncHttpClient = new SyncHttpClient();
syncHttpClient.get(url,new FileAsyncHttpResponseHandler(file) {
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
fail("never happen");
}

@Override
public void onSuccess(int statusCode, Header[] headers, File file) {
YumeFile yumeFile = new YumeFile(file.getAbsolutePath());
assertEquals("52f9b35f9f7cfa1be2644bcbac61a983",yumeFile.checksum());
assertEquals(141796,file.length());
}
});

File externalFileFolder = Robolectric.getShadowApplication().getExternalFilesDir("").getParentFile();
externalFileFolder.delete();
}

Source Code At Gists

End